#include "bsp_nm25q128.h"

#include "main.h"
#include "spi.h"

#define NM25Q128_PAGE_PROG (0x02)
#define NM25Q128_RD_DATA (0x03)
#define NM25Q128_RD_SR1 (0x05)
#define NM25Q128_WR_ENABEL (0x06)
#define NM25Q128_ERASE_SECTOR (0x20)
#define NM25Q128_RD_ID (0x90)

#define NM25Q128_SPI hspi1
#define NM25Q128_ENABLE HAL_GPIO_WritePin(NM25Q128_CS_GPIO_Port, NM25Q128_CS_Pin, GPIO_PIN_RESET)
#define NM25Q128_DISABLE HAL_GPIO_WritePin(NM25Q128_CS_GPIO_Port, NM25Q128_CS_Pin, GPIO_PIN_SET)

void nm25q128_read_id(uint16_t *id)
{
	uint8_t seqence[4] = {NM25Q128_RD_ID};

	NM25Q128_ENABLE;

	HAL_StatusTypeDef retval;

	retval = HAL_SPI_Transmit(&NM25Q128_SPI, seqence, sizeof(seqence), 1000);
	assert_param(retval == HAL_OK);

	retval = HAL_SPI_Receive(&NM25Q128_SPI, (uint8_t *)id, 2, 1000);
	assert_param(retval == HAL_OK);

	NM25Q128_DISABLE;
}

static void nm25q128_wait_busy(void)
{
	uint8_t sr1 = 0xff;
	uint8_t cmd = NM25Q128_RD_SR1;

	NM25Q128_ENABLE;

	do
	{
		if (!HAL_SPI_Transmit(&NM25Q128_SPI, &cmd, sizeof(cmd), 1000) &&
			!HAL_SPI_Receive(&NM25Q128_SPI, &sr1, sizeof(sr1), 1000))
		{
			continue;
		}

	} while (sr1 & 0x1);

	NM25Q128_DISABLE;
}

static void nm25q128_write_enable(void)
{
	uint8_t cmd = NM25Q128_WR_ENABEL;

	NM25Q128_ENABLE;

	HAL_StatusTypeDef retval;
	retval = HAL_SPI_Transmit(&NM25Q128_SPI, &cmd, sizeof(cmd), 1000);
	assert_param(retval == HAL_OK);

	NM25Q128_DISABLE;
}

void nm25q128_erase_sector(uint32_t addr)
{
	assert_param(addr >= 0x0 && addr <= 0xffffff);

	uint8_t sequence[4];
	sequence[0] = NM25Q128_ERASE_SECTOR;
	sequence[1] = (uint8_t)(addr >> 16);
	sequence[2] = (uint8_t)(addr >> 8);
	sequence[3] = (uint8_t)addr;

	nm25q128_write_enable();

	nm25q128_wait_busy();

	NM25Q128_ENABLE;

	HAL_StatusTypeDef retval;
	retval = HAL_SPI_Transmit(&NM25Q128_SPI, sequence, sizeof(sequence), 1000);
	assert_param(retval == HAL_OK);

	NM25Q128_DISABLE;

	nm25q128_wait_busy();
}

void nm25q128_page_program(uint32_t addr, const uint8_t *data, uint16_t len)
{
	assert_param(addr >= 0x0 && addr <= 0xffffff);
	assert_param(len <= 256);

	uint8_t sequence[4];
	sequence[0] = NM25Q128_PAGE_PROG;
	sequence[1] = (uint8_t)(addr >> 16);
	sequence[2] = (uint8_t)(addr >> 8);
	sequence[3] = (uint8_t)addr;

	nm25q128_write_enable();

	NM25Q128_ENABLE;

	HAL_StatusTypeDef retval;
	retval = HAL_SPI_Transmit(&NM25Q128_SPI, sequence, sizeof(sequence), 1000);
	assert_param(retval == HAL_OK);

	retval = HAL_SPI_Transmit(&NM25Q128_SPI, data, len, 1000);
	assert_param(retval == HAL_OK);

	NM25Q128_DISABLE;

	nm25q128_wait_busy();
}

void nm25q128_read_data(uint32_t addr, uint8_t *data, uint32_t len)
{
	assert_param(addr >= 0x0 && addr <= 0xffffff);

	uint8_t sequence[4];
	sequence[0] = NM25Q128_RD_DATA;
	sequence[1] = (uint8_t)(addr >> 16);
	sequence[2] = (uint8_t)(addr >> 8);
	sequence[3] = (uint8_t)addr;

	NM25Q128_ENABLE;

	HAL_StatusTypeDef retval;
	retval = HAL_SPI_Transmit(&NM25Q128_SPI, sequence, sizeof(sequence), 1000);
	assert_param(retval == HAL_OK);

	retval = HAL_SPI_Receive(&NM25Q128_SPI, data, len, 1000);
	assert_param(retval == HAL_OK);

	NM25Q128_DISABLE;
}
